﻿#region Includes
using System;
using System.Collections;
using System.Collections.Generic;
using OpenTK;
using OpenTK.Graphics.OpenGL;

using GingerGL.Math;
using GingerGL.Graphics;
#endregion

namespace GingerGL
{
    //------------------------------------------------------------//
    /// <summary>
    /// Scene Class
    /// <para>Draws All objects on screen</para>
    /// </summary>
    public class Engine
    {
        //--------------------------------------------------------//
        #region Properties
        /// <summary>
        /// Game Window
        /// </summary>
        public GameWindow GameWindow { get; private set; }
        /// <summary>
        /// Objects in Scene
        /// </summary>
        public Scene Scene { get; set; }
        /// <summary>
        /// Gets/Sets TRUE if the Scene Rendering is Paused
        /// </summary>
        public bool Paused { get; set; }
        /// <summary>
        /// Gets/Sets the Main Level or Map
        /// </summary>
        public BSP Level { get; private set; }
        /// <summary>
        /// Gets/Sets the Main Skybox
        /// </summary>
        public Skybox Skybox { get; private set; }
        /// <summary>
        /// Gets/Sets the Main Entity to Control
        /// </summary>
        public Entity MainPlayer { get; set; }
        /// <summary>
        /// Gets/Sets the Main Camera to View From
        /// </summary>
        public Camera MainCamera { get { return this.MainPlayer; } }
        /// <summary>
        /// Gets the Current Scene Frustum
        /// </summary>
        public Frustum Frustum { get { return this.MainPlayer; } }
        /// <summary>
        /// Global Scene Class
        /// </summary>
        static public Engine Instance;
        #endregion
        //--------------------------------------------------------//
        #region Constructor
        /// <summary>
        /// Initializes Scene
        /// </summary>
        public Engine() 
        {
            //game window
            this.GameWindow = new GameWindow
            (
                800, 
                600, 
                OpenTK.Graphics.GraphicsMode.Default, 
                "Ginger 3D", 
                GameWindowFlags.Default
            );            
            this.GameWindow.VSync = VSyncMode.On;
            this.GameWindow.CursorVisible = false;

            //player
            //this.MainPlayer = new Entity();
            //this.MainPlayer.Set(new Vector3(0, 1, 0));
            //this.MainPlayer.Enabled = true;            

            //toys
            this.Skybox = new Skybox();
            this.Skybox.Load(
                @"data\images\skybox\01\front.png",
                @"data\images\skybox\01\right.png",
                @"data\images\skybox\01\back.png",
                @"data\images\skybox\01\left.png",
                @"data\images\skybox\01\top.png",
                @"data\images\skybox\01\bottom.png"
            );
            //Level
            this.Level = new BSP();
            this.Level.Load(@"data\maps\test1.bsp", BSP.DefaultTesselation);

            text = new Text2D(@"data\fonts\Font.bmp");

            //mShape = Shapes.Cube(20);
            //mMap = new GridMap(50);

            //Ginger.IO.MD3.MD3[] model = IO.MD3.MD3.Load(@"data\models\md3-thetick.pk3");
            //GingerGL.Drawing.MD3.Model[] model = GingerGL.Drawing.MD3.Model.Load(@"data\models\md3-troll.pk3");
            //Ginger.IO.MD3.MD3[] model = IO.MD3.MD3.Load(@"data\models\md3-trooper.pk3");
            //GingerGL.IO.MD3.MD3[] model = IO.MD3.MD3.Load(@"data\models\md3-vadr.pk3");
            //Ginger.IO.MD3.MD3[] model = IO.MD3.MD3.Load(@"data\models\md3-sarge.pk3");
            //QuakeModel qm = new QuakeModel(model[0]);
            //qm.SetLegSequence("run");
            //qm.SetTorsoSequence("attack");
            //mObject = qm;
        }
        #endregion
        //--------------------------------------------------------//
        #region Methods
        /// <summary>
        /// Game Window was resized
        /// </summary>
        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
            // Reset The Current Viewport
            GL.Viewport(ClientRectangle.X, ClientRectangle.Y, ClientRectangle.Width, ClientRectangle.Height);
            MainCamera.Resize(this.Width, this.Height);
        }
        /// <summary>
        /// Render Scene
        /// </summary>
        protected override void OnRenderFrame(FrameEventArgs e)
        {
            #region Scene State Checks
            if (!Focused) return;                               //does the window have focus?
            if (this.Paused) return;                            //are we paused?
            //to-do, check entity states
            #endregion

            #region Setup Scene
            this.Begin(this, e);                                     //being to render scene
            #endregion

            #region Render Scene
            this.Render(e);
            #endregion

            #region  Finish Scene
            this.End(this, e);
            #endregion
        }
        /// <summary>
        /// Render Scene
        /// </summary>
        private void Render(FrameEventArgs e)
        {
            #region Player View
            this.MainPlayer.Update();                           //draw players view
            #endregion

            #region Skybox
            this.Skybox.Draw(this.MainCamera.Position);
            #endregion


            text.Draw(10, 20, "Test");

            //mMap.Draw(e);
            //mObject.Draw(e);
            //GL.Translate(new Vector3(0, 20, 100));
            //mShape.Draw(e);
            //quakemap.Draw(e);


        }
        /// <summary>
        /// Post Render Update
        /// </summary>
        protected override void OnUpdateFrame(FrameEventArgs e)
        {
            base.OnUpdateFrame(e);
            Controller.Instance.Update(this, e);
        }
        /// <summary>
        /// Initialize Scene
        /// </summary>
        protected override void OnLoad(EventArgs e) { InitializeOpenGL(); }
        /// <summary>
        /// Begin to Render the Scene
        /// </summary>
        private void Begin(object sender, FrameEventArgs e)
        {
            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);  //clear screen
            GL.LoadIdentity();                                                          //Reset The Modelview Matrix
        }
        /// <summary>
        /// End Scene
        /// </summary>
        private void End(object sender, FrameEventArgs e)
        {
            SwapBuffers();                                      //swap pixel buffers
            Title = String.Format("Position: {0} | FPS: {1} | PnY: {2}:{3}", MainPlayer.Position, System.Math.Round(RenderFrequency),MainPlayer.Pitch, MainPlayer.Yaw);
        }
        /// <summary>
        /// Initialize the OpenGL Defaults
        /// </summary>
        private void InitializeOpenGL()
        {
            GL.ShadeModel(ShadingModel.Smooth);                                 // Enable Smooth Shading
            GL.ClearColor(0.3f, 0.3f, 0.3f, 0.5f);                              // Black Background
            GL.Color4(1.0f, 1.0f, 1.0f, 1.0f);                                  // White Foreground
            GL.Enable(EnableCap.DepthTest);							            // Enables Depth Testing
            GL.Enable(EnableCap.StencilTest);                                   // Enables Stencil Testing
            GL.Enable(EnableCap.Blend);                                         // Enables Blending
            GL.Enable(EnableCap.Texture2D);                                     // Enables Textures
            GL.ClearDepth(1.0f);								                // Depth Buffer Setup
            GL.DepthFunc(DepthFunction.Lequal);								    // The Type Of Depth Testing To Do
            GL.DepthRange(0.0, 1.0);
            GL.CullFace(CullFaceMode.Front);
            GL.Enable(EnableCap.CullFace);                                      // Enables Culling Front Face
            GL.Hint(HintTarget.PerspectiveCorrectionHint, HintMode.Nicest);	    // Really Nice Perspective Calculations

            //GL.Enable(EnableCap.Lighting);
            //GL.LightModel(LightModelParameter.LightModelAmbient, new float[] { 1f, 1f, 1f, 1.0f });
            //GL.Enable(EnableCap.ColorMaterial);
            //GL.ColorMaterial(MaterialFace.Front, ColorMaterialParameter.AmbientAndDiffuse);
        }
        /// <summary>
        /// Starts Scene
        /// </summary>
        /// <param name="fps">Frames Per Second</param>
        static public void Start(int fps)
        {
            GC.Collect();
            GC.WaitForPendingFinalizers();

            Instance = new Engine();
            Instance.Run(30);
        }
        #endregion
        //--------------------------------------------------------//
    }
    //------------------------------------------------------------//
}
